x86: New vcpu_op call to get physical CPU identity.
authorKeir Fraser <keir.fraser@citrix.com>
Wed, 5 Mar 2008 10:52:51 +0000 (10:52 +0000)
committerKeir Fraser <keir.fraser@citrix.com>
Wed, 5 Mar 2008 10:52:51 +0000 (10:52 +0000)
Some AMD machines have APIC IDs that not equal to CPU IDs. In
the default Xen configuration, ACPI calls on these machines
can get confused. This shows up most noticeably when running
AMD PowerNow!. The only solution is for dom0 to get the
hypervisor's cpuid to apicid table when needed (ie, when dom0
vcpus are pinned).

Add a vcpu op to Xen to allow dom0 to query the hypervisor for
architecture dependent physical cpu information if dom0 vcpus are
pinned.

Signed-off-by: Mark Langsdorf <mark.langsdorf@amd.com>
Signed-off-by: Keir Fraser <keir.fraser@citrix.com>
xen/arch/x86/acpi/boot.c
xen/arch/x86/domain.c
xen/common/domain.c
xen/common/schedule.c
xen/include/public/vcpu.h
xen/include/xen/acpi.h
xen/include/xen/sched.h

index 24939059fbe578e55e641f141fdda270d4695315..5666a31fed51d4a72f80d88d6ff4eff4962d3711 100644 (file)
@@ -1029,3 +1029,17 @@ int __init acpi_boot_init(void)
 
        return 0;
 }
+
+unsigned int acpi_get_processor_id(unsigned int cpu)
+{
+       unsigned int acpiid, apicid;
+
+       if ((apicid = x86_cpu_to_apicid[cpu]) == 0xff)
+               return 0xff;
+
+       for (acpiid = 0; acpiid < ARRAY_SIZE(x86_acpiid_to_apicid); acpiid++)
+               if (x86_acpiid_to_apicid[acpiid] == apicid)
+                       return acpiid;
+
+       return 0xff;
+}
index 25bc2c71483494dad568426b16ad3c89f9d177c8..d841ca29ea1279f5ad26313650e2764684154d50 100644 (file)
@@ -29,6 +29,7 @@
 #include <xen/console.h>
 #include <xen/percpu.h>
 #include <xen/compat.h>
+#include <xen/acpi.h>
 #include <asm/regs.h>
 #include <asm/mc146818rtc.h>
 #include <asm/system.h>
@@ -952,6 +953,25 @@ arch_do_vcpu_op(
         break;
     }
 
+    case VCPUOP_get_physid:
+    {
+        struct vcpu_get_physid cpu_id;
+
+        rc = -EINVAL;
+        if ( !v->domain->is_pinned )
+            break;
+
+        cpu_id.phys_id = (x86_cpu_to_apicid[v->vcpu_id] |
+                          (acpi_get_processor_id(v->vcpu_id) << 8));
+
+        rc = -EFAULT;
+        if ( copy_to_guest(arg, &cpu_id, 1) )
+            break;
+
+        rc = 0;
+        break;
+    }
+
     default:
         rc = -ENOSYS;
         break;
index 112978f82c65abdecfa388c9dd2fc02f85a2aa32..e891c3893ccc6a97f9d48718261f4e0a55c750c8 100644 (file)
 #include <public/vcpu.h>
 #include <xsm/xsm.h>
 
+/* opt_dom0_vcpus_pin: If true, dom0 VCPUs are pinned. */
+static unsigned int opt_dom0_vcpus_pin;
+boolean_param("dom0_vcpus_pin", opt_dom0_vcpus_pin);
+
+enum cpufreq_controller cpufreq_controller;
+static void __init setup_cpufreq_option(char *str)
+{
+    if ( !strcmp(str, "dom0-kernel") )
+    {
+        cpufreq_controller = FREQCTL_dom0_kernel;
+        opt_dom0_vcpus_pin = 1;
+    }
+}
+custom_param("cpufreq", setup_cpufreq_option);
+
 /* Protect updates/reads (resp.) of domain_list and domain_hash. */
 DEFINE_SPINLOCK(domlist_update_lock);
 DEFINE_RCU_READ_LOCK(domlist_read_lock);
@@ -198,6 +213,9 @@ struct domain *domain_create(
     if ( domcr_flags & DOMCRF_hvm )
         d->is_hvm = 1;
 
+    if ( (domid == 0) && opt_dom0_vcpus_pin )
+        d->is_pinned = 1;
+
     rangeset_domain_initialise(d);
 
     if ( !is_idle_domain(d) )
index 5efd52b6a8ddd76a134d9079a383fb0d2b01c1f1..3e80e4b3ebcb5c70498bdf449d57640c998a10d9 100644 (file)
 static char opt_sched[10] = "credit";
 string_param("sched", opt_sched);
 
-/* opt_dom0_vcpus_pin: If true, dom0 VCPUs are pinned. */
-static unsigned int opt_dom0_vcpus_pin;
-boolean_param("dom0_vcpus_pin", opt_dom0_vcpus_pin);
-
-enum cpufreq_controller cpufreq_controller;
-static void __init setup_cpufreq_option(char *str)
-{
-    if ( !strcmp(str, "dom0-kernel") )
-    {
-        cpufreq_controller = FREQCTL_dom0_kernel;
-        opt_dom0_vcpus_pin = 1;
-    }
-}
-custom_param("cpufreq", setup_cpufreq_option);
-
 #define TIME_SLOP      (s32)MICROSECS(50)     /* allow time to slip a bit */
 
 /* Various timer handlers. */
@@ -117,7 +102,7 @@ int sched_init_vcpu(struct vcpu *v, unsigned int processor)
      * domain-0 VCPUs, are pinned onto their respective physical CPUs.
      */
     v->processor = processor;
-    if ( is_idle_domain(d) || ((d->domain_id == 0) && opt_dom0_vcpus_pin) )
+    if ( is_idle_domain(d) || d->is_pinned )
         v->cpu_affinity = cpumask_of_cpu(processor);
     else
         cpus_setall(v->cpu_affinity);
@@ -302,7 +287,7 @@ static int __vcpu_set_affinity(
 
 int vcpu_set_affinity(struct vcpu *v, cpumask_t *affinity)
 {
-    if ( (v->domain->domain_id == 0) && opt_dom0_vcpus_pin )
+    if ( v->domain->is_pinned )
         return -EINVAL;
     return __vcpu_set_affinity(v, affinity, 0, 0);
 }
index 4cd4229d915dfd241f85f5ac370ac4ce8f816c7d..b7173c6024f7637e0d2f701c224d14b2abd358ef 100644 (file)
@@ -170,7 +170,7 @@ DEFINE_XEN_GUEST_HANDLE(vcpu_set_singleshot_timer_t);
  *
  * This may be called only once per vcpu.
  */
-#define VCPUOP_register_vcpu_info   10  /* arg == struct vcpu_info */
+#define VCPUOP_register_vcpu_info   10  /* arg == vcpu_register_vcpu_info_t */
 struct vcpu_register_vcpu_info {
     uint64_t mfn;    /* mfn of page to place vcpu_info */
     uint32_t offset; /* offset within page */
@@ -182,6 +182,22 @@ DEFINE_XEN_GUEST_HANDLE(vcpu_register_vcpu_info_t);
 /* Send an NMI to the specified VCPU. @extra_arg == NULL. */
 #define VCPUOP_send_nmi             11
 
+/* 
+ * Get the physical ID information for a pinned vcpu's underlying physical
+ * processor.  The physical ID informmation is architecture-specific.
+ * On x86: id[7:0]=apic_id, id[15:8]=acpi_id, id[63:16]=mbz,
+ *         and an unavailable identifier is returned as 0xff.
+ * This command returns -EINVAL if it is not a valid operation for this VCPU.
+ */
+#define VCPUOP_get_physid           12 /* arg == vcpu_get_physid_t */
+struct vcpu_get_physid {
+    uint64_t phys_id;
+};
+typedef struct vcpu_get_physid vcpu_get_physid_t;
+DEFINE_XEN_GUEST_HANDLE(vcpu_get_physid_t);
+#define xen_vcpu_physid_to_x86_apicid(physid) ((uint8_t)((physid)>>0))
+#define xen_vcpu_physid_to_x86_acpiid(physid) ((uint8_t)((physid)>>8))
+
 #endif /* __XEN_PUBLIC_VCPU_H__ */
 
 /*
index fcb161ba927cccff59bbe414f4cdf6f216cb5bd5..39a16d1d808adede120026bddc7ec20282bba6a9 100644 (file)
@@ -447,6 +447,7 @@ extern acpi_table_handler acpi_table_ops[ACPI_TABLE_COUNT];
 
 typedef int (*acpi_madt_entry_handler) (acpi_table_entry_header *header, const unsigned long end);
 
+unsigned int acpi_get_processor_id (unsigned int cpu);
 char * __acpi_map_table (unsigned long phys_addr, unsigned long size);
 unsigned long acpi_find_rsdp (void);
 int acpi_boot_init (void);
index dc79463ba759d03ab750ea1525b8761ef5a99b51..777195e4483e448d91599134ba81b670e4b4e03e 100644 (file)
@@ -199,6 +199,8 @@ struct domain
     enum { DOMDYING_alive, DOMDYING_dying, DOMDYING_dead } is_dying;
     /* Domain is paused by controller software? */
     bool_t           is_paused_by_controller;
+    /* Domain's VCPUs are pinned 1:1 to physical CPUs? */
+    bool_t           is_pinned;
 
     /* Guest has shut down (inc. reason code)? */
     spinlock_t       shutdown_lock;